package com.gxp.framework.aspectj;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.gxp.common.annotation.Log;
import com.gxp.common.config.GxPConfig;
import com.gxp.common.constant.Constants;
import com.gxp.common.core.domain.entity.SysUser;
import com.gxp.common.enums.BusinessAction;
import com.gxp.common.utils.DateUtils;
import com.gxp.common.utils.ServletUtils;
import com.gxp.common.utils.ShiroUtils;
import com.gxp.common.utils.StringUtils;
import com.gxp.framework.manager.AsyncManager;
import com.gxp.framework.manager.factory.AsyncFactory;
import com.gxp.system.domain.ZkAudit;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;

/**
 * 操作日志记录处理
 * 
 * @author ruoyi
 */
@Aspect
@Component
public class LogAspect
{
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /** 排除敏感属性字段 */
    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

//    @Autowired
//    private SysUserMapper userMapper;
//    @Autowired
//    private ZkInstrumentMapper instrumentMapper;
//    @Autowired
//    private ZknjServiceMapper serviceMapper;

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void boBefore(JoinPoint joinPoint, Log controllerLog)
    {

        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
    {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     * 
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
    {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
    {
        try
        {
            // 获取当前的用户
            SysUser currentUser = ShiroUtils.getSysUser();

//            // *========数据库日志=========*//
//            SysOperLog operLog = new SysOperLog();
//            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
//            // 请求的地址
//            String ip = ShiroUtils.getIp();
//            operLog.setOperIp(ip);
//            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
//            if (currentUser != null)
//            {
//                operLog.setOperName(currentUser.getLoginName());
//            }
//
//            if (e != null)
//            {
//                operLog.setStatus(BusinessStatus.FAIL.ordinal());
//                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
//            }
//            // 设置方法名称
//            String className = joinPoint.getTarget().getClass().getName();
//            String methodName = joinPoint.getSignature().getName();
//            operLog.setMethod(className + "." + methodName + "()");
//            // 设置请求方式
//            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
//            // 处理设置注解上的参数
//            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
//            // 设置消耗时间
//            operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
//            // 保存数据库
//            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));

            // *========数据库日志=========*//
            ZkAudit audit = new ZkAudit();
            audit.setOperStatus(Constants.SUCCESS);
            // 请求的地址
            String ip = ShiroUtils.getIp();
            audit.setIp(ip);
            if (currentUser != null){
                audit.setWho(currentUser.getLoginName());
            }
            if(e != null){
                audit.setOperStatus(Constants.FAIL);
                audit.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            String jsonString = JSONObject.toJSONString(jsonResult);
            JSONObject jsonObject = null;

            if (StringUtils.isNotEmpty(jsonString)) {

                jsonObject = JSONObject.parseObject(jsonString);
                // 带上实体Id
                String data = jsonObject.getString("entityId");
                if (StringUtils.isNotEmpty(data)) {
                    audit.setEntityId(Long.valueOf(data));
                }
            }

            if (controllerLog.businessAction().equals(BusinessAction.APPROVED)){
                audit.setWhat(controllerLog.description() + " Status: " + jsonObject.getString("approveStatus"));
            }else {
                audit.setWhat(controllerLog.description());
            }
            audit.setTitle(controllerLog.businessTitle().getInfo());
            audit.setType(controllerLog.businessAction().ordinal());
            Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
            String params = JSONObject.toJSONString(map, excludePropertyPreFilter(controllerLog.excludeParamNames()));
            audit.setAuditParams(StringUtils.substring(params, 0, 2000));

            UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            audit.setWher(browser);
            audit.setTime(DateUtils.getNowDate());
            audit.setVersion(GxPConfig.getVersion());
            audit.setComment("");
            AsyncManager.me().execute(AsyncFactory.recordAudit(audit));
        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
        finally
        {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * 
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
//    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
//    {
//        // 设置action动作
//        operLog.setBusinessType(log.businessType().ordinal());
//        // 设置标题
//        operLog.setTitle(log.title());
//        // 设置操作人类别
//        operLog.setOperatorType(log.operatorType().ordinal());
//        // 设置操作描述
//        operLog.setOperDes(log.description());
//        // 是否需要保存request，参数和值
//        if (log.isSaveRequestData())
//        {
//            // 获取参数的信息，传入到数据库中。
//            setRequestValue(joinPoint, operLog, log.excludeParamNames(), log.idToObjParamNames());
//        }
//        // 是否需要保存response，参数和值
//        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
//        {
//            operLog.setJsonResult(StringUtils.substring(JSONObject.toJSONString(jsonResult), 0, 2000));
//        }
//    }

//    /**
//     * 获取请求的参数，放到log中
//     *
//     * @param operLog 操作日志
//     * @throws Exception 异常
//     */
//    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames, String[] idToObjParamNames) throws Exception
//    {
//        Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
//        if (StringUtils.isNotEmpty(map)) {
//            String params = JSONObject.toJSONString(map, excludePropertyPreFilter(excludeParamNames));
//            operLog.setOperParam(StringUtils.substring(idToObj(params, idToObjParamNames), 0, 2000));
//        } else {
//            Object args = joinPoint.getArgs();
//            if (StringUtils.isNotNull(args)) {
//                String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
//                operLog.setOperParam(StringUtils.substring(idToObj(params, idToObjParamNames), 0, 2000));
//            }
//        }
//    }

//    /**
//     * id替换属性
//     * @param params
//     * @return
//     */
//    private String idToObj(String params, String[] idToObjParamNames) {
//        if(StringUtils.isEmpty(idToObjParamNames)){
//            return params;
//        }
//        JSONObject jsonObject = JSONObject.parseObject(params);
//        for (String idToObjParamName : idToObjParamNames) {
//            String id = jsonObject.getString(idToObjParamName);
//            if(id == null){
//                continue;
//            }
//            List<String> ids = StringUtils.str2List(id, ",", true, true).stream()
//                    .map(p -> p.replace("\"", "").replace("[","").replace("]","")).collect(Collectors.toList());
//
//            //managers || requesters
//            if(GxPIdConstant.MANAGER.equals(idToObjParamName) || GxPIdConstant.REQUESTERS.equals(idToObjParamName)){
//                List<String> usernames = ids.stream().map(userId -> userMapper.selectUserById(Long.valueOf(userId)).getUserName()).collect(Collectors.toList());
//                jsonObject.put(idToObjParamName, String.join(",", usernames));
//            }
//            //instIds
//            if(GxPIdConstant.INSTRUMENTS.equals(idToObjParamName)){
//                List<String> instruments = ids.stream().map(instId -> instrumentMapper.selectZkInstrumentByInstId(Long.valueOf(instId)).getInstName()).collect(Collectors.toList());
//                jsonObject.remove(idToObjParamName);
//                jsonObject.put("instruments", String.join(",", instruments));
//            }
//            //services
//            if(GxPIdConstant.SERVICES.equals(idToObjParamName)){
//                List<String> services = ids.stream().map(serId -> serviceMapper.selectZknjServiceBySerId(Long.valueOf(serId)).getSerName()).collect(Collectors.toList());
//                jsonObject.put(idToObjParamName, String.join(",", services));
//            }
//
//        }
//
//        return jsonObject.toString();
//    }

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)
    {
        return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (StringUtils.isNotNull(o) && !isFilterObject(o))
                {
                    try
                    {
                        Object jsonObj = JSONObject.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     * 
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Object value : collection)
            {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
